package com.llb666.shop.items.application;

import com.alibaba.fastjson2.JSON;
import com.llb666.common.core.enums.YesOrNo;
import com.llb666.common.redis.service.RedisService;
import com.llb666.shop.items.api.vo.CarouselVo;
import com.llb666.shop.items.api.vo.CategoryVo;
import com.llb666.shop.items.api.vo.NewItemsVo;
import com.llb666.shop.items.api.vo.SimpleCategoryVo;
import com.llb666.shop.items.api.vo.SimpleItemVo;
import com.llb666.shop.items.api.vo.SubCategoryVo;
import com.llb666.shop.items.domain.entity.ItemsImg;
import com.llb666.shop.items.domain.service.CarouselService;
import com.llb666.shop.items.domain.service.CategoryService;
import com.llb666.shop.items.domain.service.ItemsService;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 首页业务逻辑应用层. fixme mysql数据库与redis中间件的分布式事务如何保障？
 *
 * @author llb666
 */
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class IndexApplication {

    private final RedisService redisService;
    private final CarouselService carouselService;
    private final CategoryService categoryService;
    private final ItemsService itemsService;

    /**
     * 获取首页轮播图列表.
     *
     * @return 首页轮播图列表
     */
    public List<CarouselVo> carousel() {
        List<CarouselVo> carouselList;
        String carouselStr = redisService.getCacheObject("carousel");
        if (StringUtils.isNotBlank(carouselStr)) {
            carouselList = JSON.parseArray(carouselStr, CarouselVo.class);
        } else {
            // 没有从缓存中获取到数据，查询数据库。
            carouselList = CarouselVo.Converter.INSTANCE.do2VoList(carouselService.queryAll(YesOrNo.YES.type));
            if (CollectionUtils.isEmpty(carouselList)) {
                redisService.setCacheObject("carousel", JSON.toJSONString(carouselList), 5L, TimeUnit.MINUTES);
            } else {
                redisService.setCacheObject("carousel", JSON.toJSONString(carouselList));
            }
        }
        return carouselList;
    }

    /**
     * 查询一级商品类目.
     *
     * @return 一级商品类目列表
     */
    public List<SimpleCategoryVo> category() {
        // 定义前端一级分类vo数组对象.
        List<SimpleCategoryVo> simpleCategoryList;
        // 先获取一级分类菜单缓存数据.
        String catsStr = redisService.getCacheObject("cats");
        // 1.如果缓存数据存在，直接从缓存数据中获取数据.
        if (StringUtils.isNotBlank(catsStr)) {
            simpleCategoryList = JSON.parseArray(catsStr, SimpleCategoryVo.class);
        } else {
            // 2.如果缓存数据不存在，将从数据库中查询数据，并将数据同步到缓存中.
            simpleCategoryList = SimpleCategoryVo.Converter.INSTANCE.do2VoList(categoryService.queryAllCategoryRoot());
            if (CollectionUtils.isEmpty(simpleCategoryList)) {
                redisService.setCacheObject("cats", JSON.toJSONString(simpleCategoryList), 5L, TimeUnit.MINUTES);
            } else {
                redisService.setCacheObject("cats", JSON.toJSONString(simpleCategoryList));
            }
        }
        return simpleCategoryList;
    }

    /**
     * 查询二级子分类商品类目.
     *
     * @param rootCatId 一级分类Id
     * @return 二级子分类商品类目列表
     */
    public List<CategoryVo> subCategory(Integer rootCatId) {
        // 定义二级分类数组对象.
        List<CategoryVo> subCatList;
        // 获取二级分类菜单缓存数据.
        String subCatStr = redisService.getCacheObject("subCat:" + rootCatId);
        if (StringUtils.isBlank(subCatStr)) {
            // 根据一级菜单id查询二级商品分类.
            subCatList = CategoryVo.Converter.INSTANCE.do2VoList(categoryService.getCategoryByRootCatId(rootCatId));
            Set<Integer> categoryTwoIds = subCatList.stream().map(CategoryVo::getId).collect(Collectors.toSet());
            // 根据二级菜单id列表查询三级级商品分类.
            List<SubCategoryVo> categoryVoThreeList = SubCategoryVo.Converter.INSTANCE
                    .do2VoList(categoryService.getCategoryByRootCatIds(categoryTwoIds));
            // 三级商品类目根据父级类目进行分组.
            Map<Integer, List<SubCategoryVo>> categoryVoMap = categoryVoThreeList.stream()
                    .collect(Collectors.groupingBy(SubCategoryVo::getSubFatherId));
            subCatList.forEach(e -> e.setSubCatList(categoryVoMap.get(e.getId())));
            /*
             * 查询的key不存在,对应的id数据库也不存在,此时被非法用户进行攻击，大量的请求穿透到数据库db上,造成宕机,从而影响整个系统，称之为缓存穿透现象
             * 解决方案： 把空的数据也缓存起来，比如空字符串，空对象，空数组
             */
            if (CollectionUtils.isEmpty(subCatList)) {
                // 当数据库位空也缓存起来，并设置缓存时间5分钟
                redisService.setCacheObject("subCat:" + rootCatId, JSON.toJSONString(subCatList), 5L, TimeUnit.MINUTES);
            } else {
                redisService.setCacheObject("subCat:" + rootCatId, JSON.toJSONString(subCatList));
            }
        } else {
            subCatList = JSON.parseArray(subCatStr, CategoryVo.class);
        }
        return subCatList;
    }

    /**
     * 首页滚动加载商品类目以及旗下商品推荐.
     *
     * @param rootCatId 一级商品分类ID
     * @return 商品类目一级商品
     */
    public List<NewItemsVo> sixNewItems(Integer rootCatId) {
        List<NewItemsVo> sixNewItemsLazy;
        String sixNewItemsStr = redisService.getCacheObject("sixNewItems:" + rootCatId);
        if (StringUtils.isBlank(sixNewItemsStr)) {
            // 实际列表只有一个商品类目 ￣□￣｜｜.
            sixNewItemsLazy = NewItemsVo.Converter.INSTANCE.do2VoList(categoryService.getCategoryByRootId(rootCatId)) ;
            Set<Integer> rootCatIdSet = sixNewItemsLazy.stream().map(NewItemsVo::getRootCatId).collect(Collectors.toSet());

            // 根据一级类目Id查询商品信息列表.
            List<SimpleItemVo> itemsVoList = SimpleItemVo.Converter.INSTANCE
                    .do2VoList(itemsService.getItemsByRootCatIdSet(rootCatIdSet));
            Set<String> itemsIdSet = itemsVoList.stream().map(SimpleItemVo::getItemId).collect(Collectors.toSet());

            // 根据商品id查询主图图片
            List<ItemsImg> imgUrls = itemsService.getImgUrlsByItemsIds(itemsIdSet);
            Map<String, String> imgMap = imgUrls.stream().collect(Collectors.toMap(ItemsImg::getItemId, ItemsImg::getUrl));
            itemsVoList.forEach(e-> e.setItemUrl(imgMap.get(e.getItemId())));
            Map<Integer, List<SimpleItemVo>> simpleItemMap = itemsVoList.stream().collect(Collectors.groupingBy(SimpleItemVo::getRootCatId));

            sixNewItemsLazy.forEach(e->e.setSimpleItemList(simpleItemMap.get(e.getRootCatId())));

            if (CollectionUtils.isEmpty(sixNewItemsLazy)) {
                redisService.setCacheObject("sixNewItems:" + rootCatId, JSON.toJSONString(sixNewItemsLazy), 5L,TimeUnit.MINUTES);
            } else {
                redisService.setCacheObject("sixNewItems:" + rootCatId, JSON.toJSONString(sixNewItemsLazy));
            }
        } else {
            sixNewItemsLazy = com.alibaba.fastjson.JSON.parseArray(sixNewItemsStr, NewItemsVo.class);
        }
        return sixNewItemsLazy;
    }
}
